#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_log.h"
#include "driver/uart.h"
#include "string.h"
#include "driver/gpio.h"

#include "agile_modbus.h"
#include "BSP.h"
#include "ModbusReg.h"



#define TXD_PIN (GPIO_NUM_10)
#define RXD_PIN (GPIO_NUM_18) 
#define RTS_PIN (GPIO_NUM_8)               //RS485发送/接收切换引脚   


#define RS485_TX_EN					gpio_set_level( RTS_PIN, 1)
#define RS485_RX_EN					gpio_set_level( RTS_PIN, 0)


#define uart                        UART_NUM_1


#define uart_close_event			20


static QueueHandle_t uart_queue;

static agile_modbus_rtu_t rtu_slave_ctx;
static uint8_t rtu_slave_send_buf[512];
static uint8_t rtu_slave_read_buf[512];


static uint8_t uart_tx_buf[2048];
static uint8_t uart_rx_buf[2048];
static volatile uint16_t uart_rx_start = 0;

static EventGroupHandle_t UartEvent;


static ModbusRtuSlaveParameter RtuSlaveParam;


extern  const char *TAG ;
/*	系统参数	*/
extern SystemParameter SysParam;
/*	系统状态	*/
extern SystemState SysState;




/**
  * @brief 串口事件处理任务
  * @param  None
  * @retval None
  */
static void UartEventTask(void *pvParameters)
{
	uart_event_t event;                //uart事件结构体
                                                       
	for (;;)
	{    
		if( uart_rx_start == 1)
		{
			//等得UART事件
			//从uart事件队列中接收一个项目并拷贝到事件结构体中 成功接收到项目将从队列中删除
			if (xQueueReceive(uart_queue, (void *)&event, (TickType_t)portMAX_DELAY) )     
			{
				switch (event.type)
				{
					case UART_FIFO_OVF:  //RX FIFO缓存区溢出
						ESP_LOGE("usart","[UART_FIFO_OVF]:The upper buffer is full causing the FIFO cache to overflow\n");

						uart_flush(uart);
						xQueueReset(uart_queue);   //将队列初始化

					break;

					case UART_DATA:    //接收到数据 
						//这里需要注意!!!!
						//ESP32默认的RX FIFO缓存区是128字节 当接收到120字节时就会触发此事件。如果需要接收大于120字节的数据，可以使用uart_event_t结构体中的timeout_flag成员来实现
						//timeout_flag UART读取超时标志，官方翻译过来：
							/*UART数据读取超时标志UART数据事件(在配置的RX输出期间没有接收到新数据)如果事件是由FIFO-full中断引起的，那么在下一个字节到来之前将没有带有超时标志的事件。
						UART_DATA事件的UART数据读取超时标志(在配置的RX TOUT期间没有接收到新数据)如果该事件是由FIFO-full中断引起的，那么在下一个字节到来之前将没有带有超时标志的事件。*/

						if (event.timeout_flag)     //发生超时。说明数据全部接收完成，可以开始处理了
						{
							xEventGroupSetBits(UartEvent,  0x01);
						}
						
					break;
					case UART_BREAK :
					

					break;
					case UART_BUFFER_FULL:

					break;
					case UART_FRAME_ERR:
						ESP_LOGE("usart","UART_FRAME_ERR");
						uart_flush(uart);
					break;
					case UART_PARITY_ERR:
						ESP_LOGE("usart","UART_PARITY_ERR");
						uart_flush(uart);
					break;
					case UART_DATA_BREAK:

					break;
					case UART_PATTERN_DET:

					break;

					default:
					break;
				}
			}
		}
		else
		{
			vTaskDelay(100 / portTICK_PERIOD_MS);
		}
	}
}



/**
	* @brief 引脚功能配置
	* @retval none
	*/																		
static void IO_Init( void )
{
	//zero-initialize the config structure.
    gpio_config_t io_conf = {};

    //disable interrupt
    io_conf.intr_type = GPIO_INTR_DISABLE;
    //set as output mode
    io_conf.mode = GPIO_MODE_OUTPUT;
    //bit mask of the pins that you want to set,e.g.GPIO18/19
    io_conf.pin_bit_mask = (1ULL<<RTS_PIN) ;
    //disable pull-down mode
    io_conf.pull_down_en = 0;
    //disable pull-up mode
    io_conf.pull_up_en = 0;
    //configure GPIO with the given settings
    gpio_config(&io_conf);
	
	RS485_RX_EN;		//485芯片接收使能
}



/**
  * @brief  串口配置
  * @param  NONE
  * @retval NONE
  */
static void UART_Config( void )
{
	uint8_t temp = 0;

    uart_config_t uart_config = { 0 };

	uart_config.baud_rate = RtuSlaveParam.Baudrate;
	uart_config.data_bits = UART_DATA_8_BITS;

	if( RtuSlaveParam.Parity == 0 ) uart_config.parity = UART_PARITY_DISABLE;
	else if( RtuSlaveParam.Parity == 1 ) uart_config.parity = UART_PARITY_ODD;
	else if( RtuSlaveParam.Parity == 2 ) uart_config.parity = UART_PARITY_EVEN;

	if( RtuSlaveParam.StopBits == 1 ) uart_config.stop_bits = UART_STOP_BITS_1;
	else if( RtuSlaveParam.StopBits == 2 ) uart_config.stop_bits = UART_STOP_BITS_2;
	else uart_config.stop_bits = UART_STOP_BITS_1_5;

	uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
	uart_config.source_clk = UART_SCLK_DEFAULT;


    uart_driver_install(uart, 2048, 2048, 20, &uart_queue, 0);
    uart_param_config(uart, &uart_config);

	if( RtuSlaveParam.T35Time == 0 )
	{
		if( RtuSlaveParam.Baudrate >= 115200 ) uart_set_rx_timeout( uart, 40 );
		else if( (RtuSlaveParam.Baudrate >= 57600) && (uart_config.baud_rate < 115200) ) uart_set_rx_timeout( uart, 20 );
		else if( (RtuSlaveParam.Baudrate >= 38400) && (uart_config.baud_rate < 57600) ) uart_set_rx_timeout( uart, 15 );
		else if( (RtuSlaveParam.Baudrate >= 19200) && (uart_config.baud_rate < 38400) ) uart_set_rx_timeout( uart, 6 );
		else  uart_set_rx_timeout( uart, 5 );
	}
	else
	{
		temp = RtuSlaveParam.T35Time * RtuSlaveParam.Baudrate / 10000;
		uart_set_rx_timeout( uart, temp );
	}
	
    uart_set_pin(uart, TXD_PIN, RXD_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);

	uart_rx_start = 1;
}



/**
  * @brief  关闭串口通讯
  * @param  NONE
  * @retval NONE
  */
static void UART_Close( void )
{
	uart_event_t event;

	uart_rx_start = 0;

	event.type = uart_close_event;
	event.size = 1;
	event.timeout_flag = 0;
	xQueueSend( uart_queue, &event, 0 );

	vTaskDelay(1000 / portTICK_PERIOD_MS);

	uart_driver_delete(uart);
}



/**
  * @brief  发送数据
  * @param  buf：数据
  * @param  len：数据长度
  * @retval 
  *     - ESP_OK   
  *     - ESP_FAIL 
  */
static esp_err_t RtuSlaveSendData( uint8_t *buf, uint16_t len )
{
	uint32_t timeout;
    int txBytes = 0;
    esp_err_t err;
	
	if( len > 512 ) return ESP_FAIL;
	
	memcpy( uart_tx_buf, buf, len );
	
	timeout = (len * 11 * 1000 / RtuSlaveParam.Baudrate) + 100;

    txBytes = uart_write_bytes(uart, uart_tx_buf, len);
    if( (txBytes < 0) || (txBytes != len) )
    {
        ESP_LOGE(TAG, "Rtu Slave Send Data err!!!");
        return ESP_FAIL;
    }

    err = uart_wait_tx_done( uart, (timeout/portTICK_PERIOD_MS) );
    if( err != ESP_OK )
    {
        ESP_LOGE(TAG, "Rtu Slave Send Data timeout!!!");
        return ESP_FAIL;
    }
	
	return ESP_OK;
}




/**
  * @brief 启动数据接收
  * @param  none
  * @retval none
  */
static void RtuSlaveRecvStart( void )
{
	xEventGroupClearBits( UartEvent,  0x01);    	//清除接收完成事件标志

	xQueueReset(uart_queue);						//清空消息队列
	
	uart_flush(uart);

	RS485_RX_EN;
}



/**
  * @brief 接收数据
  * @param  len：接收数据长度
  * @param  timeout：接收超时时间，单位：ms
  * @retval 返回接收数据地址
  */
uint8_t *RtuSlaveRecvData( uint16_t *len, uint32_t timeout )
{
	uint8_t *ptr = NULL;
    EventBits_t xEventGroupValue;
    int num = 0;
	size_t temp = 0;

    xEventGroupValue = xEventGroupWaitBits( UartEvent,
											0x01,
											pdTRUE,
											pdFALSE,
											(timeout/portTICK_PERIOD_MS) );

    if( (xEventGroupValue & 0x01) == 0 )
    {
        return ptr;
    }

	uart_get_buffered_data_len(uart, &temp);
    
    num = uart_read_bytes( uart, uart_rx_buf, temp, (100/portTICK_PERIOD_MS));
    if( num <= 0 )
    {
		*len = 0;
		ptr = uart_rx_buf;
	}
	else
	{
		*len = num;
		ptr = uart_rx_buf;
	}

	return ptr;
}




/**
  * @brief 初始化ModbusRtu从机
  * @retval none
*/	
void ModbusRtuSlaveInit( void )
{
	IO_Init( );         //引脚功能配置

    UartEvent = xEventGroupCreate( );

	memcpy( &RtuSlaveParam, &SysParam.ModbusRtuSlaveParam, sizeof(ModbusRtuSlaveParameter) );
		
	/*	初始化ModbusRtu协议栈 	*/
	agile_modbus_rtu_init( &rtu_slave_ctx, rtu_slave_send_buf, sizeof(rtu_slave_send_buf), rtu_slave_read_buf, sizeof(rtu_slave_read_buf) );
	agile_modbus_set_slave(&rtu_slave_ctx._ctx, (SysParam.WorkParam.DeviceType+20));

	xTaskCreate(UartEventTask, "uart event task", 1024 * 2, NULL, 9, NULL);
}




/**
 * @brief   rtu Slave callback function
 * @param   ctx modbus handle
 * @param   slave_info slave information body
 * @param   data private data
 * @return  =0: normal;
 *          <0: Abnormal
 *             (-AGILE_MODBUS_EXCEPTION_UNKNOW(-255): Unknown exception, the slave will not package the response data)
 *             (Other negative exception codes: package exception response data from the opportunity)
 */
static int ModbusRtuSlaveCallback (agile_modbus_t *ctx, struct agile_modbus_slave_info *slave_info, const void *data)
{
	int32_t function = slave_info->sft->function;
  	int32_t ret = 0;
	int32_t send_index;
	int32_t address;
	int32_t nb;
	
	switch (function)
	{
		//读线圈寄存器
		case AGILE_MODBUS_FC_READ_COILS:
		{
			address = slave_info->address;
			nb = slave_info->nb;
			send_index = slave_info->send_index;
			
			ret = eMBRegCoilsCB( (ctx->send_buf + send_index), address, nb, 0 );
		}
		break;
	
		//读离散输入寄存器
		case AGILE_MODBUS_FC_READ_DISCRETE_INPUTS:
		{
			address = slave_info->address;
			nb = slave_info->nb;
			send_index = slave_info->send_index;
			ret = eMBRegDiscreteCB( (ctx->send_buf + send_index), address, nb );
		}
		break;

		//读保持寄存器
		case AGILE_MODBUS_FC_READ_HOLDING_REGISTERS:
		{
			address = slave_info->address;
			send_index = slave_info->send_index;
			nb = slave_info->nb;
			
			ret = eMBRegHoldingCB( (ctx->send_buf + send_index), address, nb, 0 );
		}
		break;
		
		//读输入寄存器
		case AGILE_MODBUS_FC_READ_INPUT_REGISTERS:
		{
			address = slave_info->address;
			nb = slave_info->nb;
			send_index = slave_info->send_index;
			
			ret = eMBRegInputCB( (ctx->send_buf + send_index), address, nb );
		}
		break;

		//写线圈寄存器
		case AGILE_MODBUS_FC_WRITE_SINGLE_COIL:
		case AGILE_MODBUS_FC_WRITE_MULTIPLE_COILS:
		{
			address = slave_info->address;
			
			if (function == AGILE_MODBUS_FC_WRITE_SINGLE_COIL) nb = 1;
			else nb = slave_info->nb;
			
			ret = eMBRegCoilsCB( slave_info->buf, address, nb, 1 );
		}
		break;

		//写保持寄存器
		case AGILE_MODBUS_FC_WRITE_SINGLE_REGISTER:
		case AGILE_MODBUS_FC_WRITE_MULTIPLE_REGISTERS:
		{
			address = slave_info->address;
			
			if (function == AGILE_MODBUS_FC_WRITE_SINGLE_REGISTER)
			{
				nb = 1;
			}
			else
			{
				nb = slave_info->nb;
			}
			
			ret = eMBRegHoldingCB( slave_info->buf, address, nb, 1 );
		}
		break;

//	case AGILE_MODBUS_FC_MASK_WRITE_REGISTER:
//	{
////			int address = slave_info->address;
////			if (address >= 0 && address < TAB_MAX_NUM)
////			{
////					int index = address - 0;
////					uint16_t data = _tab_registers[index];
////					uint16_t and = (slave_info->buf[0] << 8) + slave_info->buf[1];
////					uint16_t or = (slave_info->buf[2] << 8) + slave_info->buf[3];

////					data = (data & and) | (or &(~and));

////					_tab_registers[index] = data;
////			}
//	}
//	break;

//	case AGILE_MODBUS_FC_WRITE_AND_READ_REGISTERS:
//	{
////			int address = slave_info->address;
////			int nb = (slave_info->buf[0] << 8) + slave_info->buf[1];
////			uint16_t address_write = (slave_info->buf[2] << 8) + slave_info->buf[3];
////			int nb_write = (slave_info->buf[4] << 8) + slave_info->buf[5];
////			int send_index = slave_info->send_index;

////			/* Write first. 7 is the offset of the first values to write */
////			for (int now_address = address_write, i = 0; now_address < address_write + nb_write; now_address++, i++)
////			{
////					if (now_address >= 0 && now_address < TAB_MAX_NUM)
////					{
////							int index = now_address - 0;
////							uint16_t data = agile_modbus_slave_register_get(slave_info->buf + 7, i);
////							_tab_registers[index] = data;
////					}
////			}

////			/* and read the data for the response */
////			for (int now_address = address, i = 0; now_address < address + nb; now_address++, i++)
////			{
////					if (now_address >= 0 && now_address < TAB_MAX_NUM)
////					{
////							int index = now_address - 0;
////							agile_modbus_slave_register_set(ctx->send_buf + send_index, i, _tab_registers[index]);
////					}
////			}
//	}
//	break;

		default:
				ret = -AGILE_MODBUS_EXCEPTION_ILLEGAL_FUNCTION;
				break;
		}

  return ret;                    
}





/**
  * @brief Modbus Rtu从机数据处理
  * @retval none
*/	
void ModbusRtuSlaveProcess( void )
{
	static uint8_t state = 0;
	static uint32_t timeout_cnt = 0;
	uint16_t len = 0;
	uint8_t *ptr = NULL;
	int32_t send_len;
	
	switch( state )
	{
		case 0:
		{
			UART_Config( );				//串口配置
			state = 1;
		}
		break;
		
		case 1:
		{
			RtuSlaveRecvStart( );		//启动数据接收
			state = 2;
		}
		break;
		
		case 2:
		{
			len = 0;
			ptr = RtuSlaveRecvData( &len, 1000 );
			
			if( ptr != NULL )
			{
				// ESP_LOGI("uart", "receive %d bytes", len);

				if( len == 0 ) 
				{
					timeout_cnt++;
				}
				else 
				{
					timeout_cnt = 0;
				}
				
				if( len >= 8 && len <= 256 ) 
				{
					memcpy( rtu_slave_read_buf, ptr, len );
					
					send_len = agile_modbus_slave_handle( &rtu_slave_ctx._ctx, len, 1, ModbusRtuSlaveCallback, NULL, NULL );
					
					if( send_len > 0 ) 
					{
						vTaskDelay(RtuSlaveParam.TxdDelay / portTICK_PERIOD_MS);
						
						RS485_TX_EN;
						
						vTaskDelay(1 / portTICK_PERIOD_MS);
						
						RtuSlaveSendData( rtu_slave_ctx._ctx.send_buf, send_len );
						
						vTaskDelay(1 / portTICK_PERIOD_MS);
						
						RS485_RX_EN;
					}
				}
				
				state = 1;
			}	
			else
			{
				timeout_cnt++;
			}
			
			/*	2分钟内未接收任何数据，复位通信接口 */
			if( (timeout_cnt * 1000) >= 120000 )
			{
				timeout_cnt = 0;
				UART_Close( );					//关闭串口通讯
				
				vTaskDelay(1000 / portTICK_PERIOD_MS);
				
				state = 0;
			}
		}
		break;
	}
}






